package site.wlwsjsx.iot_backend.mapper;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import site.wlwsjsx.iot_backend.entity.DeviceEvent;
import site.wlwsjsx.iot_backend.mapper.DeviceEventMapper;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

/**
 * DeviceEventMapper 集成测试
 * 依赖已配置的实际数据库，测试真实SQL执行逻辑
 */
@SpringBootTest
@Transactional // 测试完成后自动回滚数据，避免污染数据库
public class DeviceEventMapperTest {

    @Autowired
    private DeviceEventMapper deviceEventMapper;

    // 测试数据：2条不同设备、不同类型的事件
    private DeviceEvent testEvent1;
    private DeviceEvent testEvent2;
    private LocalDateTime testBaseTime; // 基准时间，用于时间范围查询

    /**
     * 每个测试方法执行前：初始化测试数据并插入数据库
     */
    @BeforeEach
    void setUp() {
        testBaseTime = LocalDateTime.of(2025, 10, 15, 14, 0, 0);

        // 测试数据1：设备001，状态变更事件
        testEvent1 = new DeviceEvent();
        testEvent1.setDeviceId("dev-001");
        testEvent1.setEventIdentifier("status_update");
        testEvent1.setEventType("device_status_change");
        testEvent1.setEventData("{\"trigger\":\"timer\"}");
        testEvent1.setTemperature(26.5f);
        testEvent1.setHumidity(52.0f);
        testEvent1.setPowerStatus(1); // 1=开启
        testEvent1.setLightStatus(0); // 0=关闭
        testEvent1.setProductId("prod-001");
        testEvent1.setMessageType("MQTT");
        testEvent1.setNotifyType("none");
        testEvent1.setCreateTime(testBaseTime);
        testEvent1.setIsDeleted(0); // 0=未删除
        deviceEventMapper.insert(testEvent1); // 插入数据库，自动生成id

        // 测试数据2：设备002，传感器上报事件
        testEvent2 = new DeviceEvent();
        testEvent2.setDeviceId("dev-002");
        testEvent2.setEventIdentifier("sensor_report");
        testEvent2.setEventType("sensor_data_report");
        testEvent2.setEventData("{\"interval\":300}");
        testEvent2.setSmokeScope(0.1f);
        testEvent2.setFlameScope(0.0f);
        testEvent2.setHeatScope(25.8f);
        testEvent2.setProductId("prod-001");
        testEvent2.setMessageType("HTTP");
        testEvent2.setNotifyType("app_push");
        testEvent2.setCreateTime(testBaseTime.plusMinutes(10)); // 比testEvent1晚10分钟
        testEvent2.setIsDeleted(0);
        deviceEventMapper.insert(testEvent2); // 插入数据库，自动生成id

        // 验证插入成功（id不为null）
        assertNotNull(testEvent1.getId(), "插入后testEvent1的id不应为null");
        assertNotNull(testEvent2.getId(), "插入后testEvent2的id不应为null");
    }

    /**
     * 每个测试方法执行后：清理测试数据（因@Transactional会自动回滚，此方法可作为双重保障）
     */
    @AfterEach
    void tearDown() {
        if (testEvent1.getId() != null) {
            deviceEventMapper.deleteById(testEvent1.getId());
        }
        if (testEvent2.getId() != null) {
            deviceEventMapper.deleteById(testEvent2.getId());
        }
    }

    // ------------------------------ 测试新增方法 ------------------------------
    @Test
    void testInsert() {
        // 1. 准备新的测试数据
        DeviceEvent newEvent = new DeviceEvent();
        newEvent.setDeviceId("dev-003");
        newEvent.setEventIdentifier("fault_alert");
        newEvent.setEventType("device_fault");
        newEvent.setCreateTime(LocalDateTime.now());
        newEvent.setIsDeleted(0);

        // 2. 执行插入
        int affectRows = deviceEventMapper.insert(newEvent);

        // 3. 验证结果
        assertEquals(1, affectRows, "新增事件应影响1行数据"); // 验证受影响行数
        assertNotNull(newEvent.getId(), "新增后应自动生成id"); // 验证id生成

        // 4. 反向验证：查询新增的数据是否存在
        DeviceEvent queryResult = deviceEventMapper.selectLatestByDeviceId("dev-003");
        assertNotNull(queryResult, "应查询到dev-003的最新事件");
        assertEquals("fault_alert", queryResult.getEventIdentifier());
    }

    // ------------------------------ 测试单条件查询 ------------------------------
    @Test
    void testSelectLatestByDeviceId() {
        // 场景1：查询存在的设备（dev-002）
        DeviceEvent latestDev002 = deviceEventMapper.selectLatestByDeviceId("dev-002");
        assertNotNull(latestDev002, "应查询到dev-002的最新事件");
        assertEquals("sensor_report", latestDev002.getEventIdentifier()); // 匹配testEvent2的标识符
        assertEquals(testBaseTime.plusMinutes(10), latestDev002.getCreateTime()); // 时间最晚

        // 场景2：查询不存在的设备（dev-999）
        DeviceEvent nonExistentDev = deviceEventMapper.selectLatestByDeviceId("dev-999");
        assertNull(nonExistentDev, "不存在的设备应返回null");
    }

    @Test
    void testSelectByDeviceIdAndIdentifier() {
        // 场景1：匹配设备+标识符（dev-001 + status_update）
        List<DeviceEvent> matchList = deviceEventMapper.selectByDeviceIdAndIdentifier("dev-001", "status_update");
        assertFalse(matchList.isEmpty(), "应查询到匹配的事件列表");
        assertEquals(1, matchList.size(), "匹配条件应返回1条数据");
        assertEquals("dev-001", matchList.get(0).getDeviceId());

        // 场景2：不匹配的标识符（dev-001 + sensor_report）
        List<DeviceEvent> noMatchList = deviceEventMapper.selectByDeviceIdAndIdentifier("dev-001", "sensor_report");
        assertTrue(noMatchList.isEmpty(), "不匹配的标识符应返回空列表");
    }

    @Test
    void testSelectByTimeRange() {
        // 时间范围：testBaseTime前5分钟 ~ testBaseTime后5分钟（仅包含testEvent1）
        LocalDateTime startTime = testBaseTime.minusMinutes(5);
        LocalDateTime endTime = testBaseTime.plusMinutes(5);

        List<DeviceEvent> timeRangeList = deviceEventMapper.selectByTimeRange(startTime, endTime);
        assertEquals(1, timeRangeList.size(), "时间范围内应仅包含testEvent1");
        assertEquals("dev-001", timeRangeList.get(0).getDeviceId());
    }

    // ------------------------------ 测试统计方法 ------------------------------
    @Test
    void testCountByCondition() {
        // 场景1：多条件组合（产品prod-001 + 事件类型device_status_change）
        Long comboCount = deviceEventMapper.countByCondition(
                null,          // deviceId：不限制
                null,          // eventIdentifier：不限制
                "device_status_change", // eventType：状态变更
                "prod-001",    // productId：产品001
                null,          // startTime：不限制
                null           // endTime：不限制
        );
        assertEquals(1, comboCount, "组合条件应返回1条数据（仅testEvent1）");

        // 场景2：时间范围+设备（dev-002 + 晚于testBaseTime）
        Long timeDeviceCount = deviceEventMapper.countByCondition(
                "dev-002",     // deviceId：dev-002
                null,
                null,
                null,
                testBaseTime.plusMinutes(5), // startTime：testBaseTime后5分钟
                null
        );
        assertEquals(1, timeDeviceCount, "时间+设备条件应返回1条数据（testEvent2）");

        // 场景3：无匹配条件（设备dev-999）
        Long noMatchCount = deviceEventMapper.countByCondition(
                "dev-999",     // 不存在的设备
                null,
                null,
                null,
                null,
                null
        );
        assertEquals(0, noMatchCount, "无匹配条件应返回0");
    }

    @Test
    void testCountByDeviceId() {
        // 场景1：统计存在的设备（dev-001）
        Long dev001Count = deviceEventMapper.countByDeviceId("dev-001");
        assertEquals(1, dev001Count, "dev-001应存在1条事件");

        // 场景2：统计不存在的设备（dev-999）
        Long dev999Count = deviceEventMapper.countByDeviceId("dev-999");
        assertEquals(0, dev999Count, "dev-999应存在0条事件");
    }

    // ------------------------------ 测试更新方法 ------------------------------
    @Test
    void testUpdateProcessedStatus() {
        // 1. 执行更新（将testEvent1的处理状态设为true）
        int affectRows = deviceEventMapper.updateProcessedStatus(testEvent1.getId(), true);
        assertEquals(1, affectRows, "更新应影响1行数据");

        // 2. 验证更新结果（查询更新后的事件）
        DeviceEvent updatedEvent = deviceEventMapper.selectLatestByDeviceId("dev-001");
        assertTrue(updatedEvent.getProcessed(), "更新后处理状态应为true");
    }

    @Test
    void testBatchUpdateProcessedStatus() {
        // 1. 准备要批量更新的ID列表（testEvent1和testEvent2的id）
        List<Long> ids = Arrays.asList(testEvent1.getId(), testEvent2.getId());

        // 2. 执行批量更新（处理状态设为false）
        int affectRows = deviceEventMapper.batchUpdateProcessedStatus(ids, false);
        assertEquals(2, affectRows, "批量更新应影响2行数据");

        // 3. 验证结果
        DeviceEvent updated1 = deviceEventMapper.selectLatestByDeviceId("dev-001");
        DeviceEvent updated2 = deviceEventMapper.selectLatestByDeviceId("dev-002");
        assertFalse(updated1.getProcessed(), "testEvent1处理状态应为false");
        assertFalse(updated2.getProcessed(), "testEvent2处理状态应为false");
    }

    // ------------------------------ 测试删除方法 ------------------------------
    @Test
    void testDeleteById() {
        // 1. 执行删除（删除testEvent1）
        int affectRows = deviceEventMapper.deleteById(testEvent1.getId());
        assertEquals(1, affectRows, "删除应影响1行数据");

        // 2. 验证结果（查询已删除的事件，应返回null）
        DeviceEvent deletedEvent = deviceEventMapper.selectLatestByDeviceId("dev-001");
        assertNull(deletedEvent, "删除后dev-001的最新事件应不存在");
    }

    @Test
    void testDeleteByTimeBefore() {
        // 修复：删除时间设为 testBaseTime.plusMinutes(5)（14:05:00）
        // 此时 testEvent1的创建时间（14:00:00）< 14:05:00，会被删除；
        // testEvent2的创建时间（14:10:00）> 14:05:00，不会被删除
        LocalDateTime deleteTime = testBaseTime.plusMinutes(5);
        int affectRows = deviceEventMapper.deleteByTimeBefore(deleteTime);

        // 验证结果：仅testEvent1被删除，testEvent2仍存在
        assertEquals(1, affectRows, "删除应影响1行数据（testEvent1）");
        assertNull(deviceEventMapper.selectLatestByDeviceId("dev-001"), "testEvent1应被删除");
        assertNotNull(deviceEventMapper.selectLatestByDeviceId("dev-002"), "testEvent2应未被删除");
    }
}